﻿using System;
using System.Collections.Generic;
using System.Text;
using OAuth;
using System.Xml;
using Newtonsoft.Json;

namespace Tweets.Library
{
    /// <summary>
    /// Twitter REST wrapper class.
    /// </summary>
    public class Twitter : TwitterBase
    {
        /// <summary>
        /// Constructor requiring configuration.
        /// </summary>
        /// <param name="config">Configuration.</param>
        public Twitter(OAuthConfig config)
            : base(config) { }

        /// <summary>
        /// Test the OAuth keys to see if they are valid using Twitter's XML URL.
        /// </summary>
        /// <returns>If valid, the user associated with the keys is returned. If not valid, null is returned.</returns>
        /// <remarks>As of March 2013, XML will no longer be supported (http://mashable.com/2012/09/05/twitter-api-rss/)</remarks>
        private Tweets.Library.TwitterUser authenticateXml(OAuthConsumer oauthConsumer)
        {
            Tweets.Library.TwitterUser tweetUser = null;

            // Make an API Call (call the home_timeline status) and debug the response
            var dom = (XmlDocument)oauthConsumer.Request("http://twitter.com/account/verify_credentials.xml", "GET", new List<QueryParameter>(), ResponseFormat.Xml);
            if (dom != null)
            {
                // Check to see if there is a User
                var userNode = dom.SelectSingleNode("/user");
                if (userNode != null)
                {
                    var node = userNode.SelectSingleNode("id");
                    tweetUser = new TwitterUser();
                    tweetUser.Id = long.Parse(node.InnerText);
                    node = userNode.SelectSingleNode("name");
                    tweetUser.Name = node.InnerText;
                    node = userNode.SelectSingleNode("screen_name");
                    tweetUser.ScreenName = node.InnerText;
                    node = userNode.SelectSingleNode("location");
                    tweetUser.Location = node.InnerText;
                    node = userNode.SelectSingleNode("profile_image_url");
                    tweetUser.ProfileImageUrl = node.InnerText;
                    node = userNode.SelectSingleNode("description");
                    tweetUser.Description = node.InnerText;
                    node = userNode.SelectSingleNode("created_at");
                    tweetUser.CreatedOn = TwitterBase.ParseTwitterDate(node.InnerText);

                    return tweetUser;
                }

            } // if (dom != null)

            return null;
        }

        /// <summary>
        /// Test the OAuth keys to see if they are valid using Twitter's JSON URL.
        /// </summary>
        /// <returns>If valid, the user associated with the keys is returned. If not valid, null is returned.</returns>
        /// <remarks>As of March 2013, XML will no longer be supported (http://mashable.com/2012/09/05/twitter-api-rss/)</remarks>
        private Tweets.Library.TwitterUser authenticateJson(OAuthConsumer oauthConsumer)
        {
            TwitterUser tweetUser = null;

            // Make an API Call (call the home_timeline status) and debug the response
            string json = (string)oauthConsumer.Request("http://api.twitter.com/1/account/verify_credentials.json", "GET", new List<QueryParameter>(), ResponseFormat.Json);
            if (string.IsNullOrEmpty(json) == false)
            {
                tweetUser = JsonConvert.DeserializeObject<TwitterUser>(json);

                return tweetUser;

            } // if (string.IsNullOrEmpty(j...

            return null;
        }

        /// <summary>
        /// Test the OAuth keys to see if they are valid.
        /// </summary>
        /// <returns>If valid, the user associated with the keys is returned. If not valid, null is returned.</returns>
        public Tweets.Library.TwitterUser Authenticate()
        {
            // Create an OAuth consumer
            var oauthConsumer = new OAuthConsumer(base.Configuration, "console");

            TwitterUser tweetUser = this.authenticateJson(oauthConsumer);

            return tweetUser;
        }

        /// <summary>
        /// Get timeline data in XML.
        /// </summary>
        /// <param name="oauthConsumer">Consumer reference.</param>
        /// <param name="paramItems">Parameter items.</param>
        /// <returns>Array of tweets and related items.</returns>
        /// <remarks>As of March 2013, XML will no longer be supported (http://mashable.com/2012/09/05/twitter-api-rss/)</remarks>
        private Tweets.Library.Tweet[] getTimelineInXml(OAuthConsumer oauthConsumer, List<QueryParameter> paramItems)
        {
            Tweets.Library.Tweet tweet;
            Tweets.Library.TwitterUser tweetUser;
            Tweets.Library.TwitterHashtag tweetHashTag;
            Tweets.Library.TwitterMedia tweetMedia;
            Tweets.Library.TwitterUserMention tweetUserMention;
            Tweets.Library.TwitterUrl tweetUrl;

            // All tweets
            var tweetsList = new List<Tweets.Library.Tweet>();

            // Make an API Call (call the home_timeline status) and debug the response
            var dom = (XmlDocument)oauthConsumer.Request("http://api.twitter.com/1/statuses/user_timeline.xml", "GET", paramItems, ResponseFormat.Xml);
            if (dom != null)
            {
                // Get all tweets
                XmlNodeList statusNodes = dom.SelectNodes("/statuses/status");
                foreach (XmlNode statusNode in statusNodes)
                {
                    // Each tweet
                    XmlNode node = statusNode.SelectSingleNode("created_at");
                    tweet = new Tweet();
                    tweetsList.Add(tweet);

                    tweet.CreatedOn = TwitterBase.ParseTwitterDate(node.InnerText);
                    node = statusNode.SelectSingleNode("id");
                    tweet.Id = long.Parse(node.InnerText);
                    node = statusNode.SelectSingleNode("text");
                    tweet.Text = node.InnerText;
                    node = statusNode.SelectSingleNode("truncated");
                    tweet.Truncated = bool.Parse(node.InnerText);

                    // User
                    var userNode = statusNode.SelectSingleNode("user");
                    node = userNode.SelectSingleNode("id");
                    tweetUser = new TwitterUser();
                    tweetUser.Id = long.Parse(node.InnerText);
                    node = userNode.SelectSingleNode("name");
                    tweetUser.Name = node.InnerText;
                    node = userNode.SelectSingleNode("screen_name");
                    tweetUser.ScreenName = node.InnerText;
                    node = userNode.SelectSingleNode("location");
                    tweetUser.Location = node.InnerText;
                    node = userNode.SelectSingleNode("profile_image_url");
                    tweetUser.ProfileImageUrl = node.InnerText;
                    node = userNode.SelectSingleNode("description");
                    tweetUser.Description = node.InnerText;
                    node = userNode.SelectSingleNode("created_at");
                    tweetUser.CreatedOn = TwitterBase.ParseTwitterDate(node.InnerText);
                    tweet.User = tweetUser;

                    var entitiesNode = statusNode.SelectSingleNode("entities");

                    // Hashtags                
                    var hashtagNodes = entitiesNode.SelectNodes("hashtags/hashtag");
                    var hashTagList = new List<TwitterHashtag>();
                    foreach (XmlNode hashtagNode in hashtagNodes)
                    {
                        node = hashtagNode.SelectSingleNode("text");
                        tweetHashTag = new TwitterHashtag();
                        tweetHashTag.Text = node.InnerText;

                        hashTagList.Add(tweetHashTag);
                    }
                    tweet.Entities.HashTagCollection = hashTagList.Count > 0 ? hashTagList.ToArray() : null;

                    // Media
                    var creativeNodes = entitiesNode.SelectNodes("media/creative");
                    var mediaList = new List<TwitterMedia>();
                    foreach (XmlNode creativeNode in creativeNodes)
                    {
                        node = creativeNode.SelectSingleNode("id");
                        tweetMedia = new TwitterMedia()
                        {
                            Id = long.Parse(node.InnerText)
                        };
                        node = creativeNode.SelectSingleNode("media_url");
                        tweetMedia.MediaUrl = node.InnerText;
                        node = creativeNode.SelectSingleNode("url");
                        tweetMedia.Url = node.InnerText;
                        node = creativeNode.SelectSingleNode("display_url");
                        tweetMedia.DisplayUrl = node.InnerText;
                        node = creativeNode.SelectSingleNode("expanded_url");
                        tweetMedia.ExpandedUrl = node.InnerText;
                        node = creativeNode.SelectSingleNode("type");
                        tweetMedia.Type = node.InnerText;

                        mediaList.Add(tweetMedia);
                    }
                    tweet.Entities.MediaCollection = mediaList.Count > 0 ? mediaList.ToArray() : null;

                    // User Mentions
                    var userMentionNodes = entitiesNode.SelectNodes("user_mentions/user_mention");
                    var userMentionList = new List<TwitterUserMention>();
                    foreach (XmlNode userMentionNode in userMentionNodes)
                    {
                        node = userMentionNode.SelectSingleNode("id");
                        tweetUserMention = new TwitterUserMention()
                        {
                            Id = long.Parse(node.InnerText)
                        };
                        node = userMentionNode.SelectSingleNode("screen_name");
                        tweetUserMention.ScreenName = node.InnerText;
                        node = userMentionNode.SelectSingleNode("name");
                        tweetUserMention.Name = node.InnerText;

                        userMentionList.Add(tweetUserMention);
                    }
                    tweet.Entities.UserMentionCollection = userMentionList.Count > 0 ? userMentionList.ToArray() : null;

                    // User Mentions
                    var urlNodes = entitiesNode.SelectNodes("urls/url");
                    var urlList = new List<TwitterUrl>();
                    foreach (XmlNode urlNode in urlNodes)
                    {
                        tweetUrl = new TwitterUrl();
                        node = urlNode.SelectSingleNode("url");
                        tweetUrl.Url = node.InnerText;
                        node = urlNode.SelectSingleNode("expanded_url");
                        tweetUrl.ExpandedUrl = node.InnerText;
                        node = urlNode.SelectSingleNode("display_url");
                        tweetUrl.DisplayUrl = node.InnerText;

                        urlList.Add(tweetUrl);
                    }
                    tweet.Entities.UrlCollection = urlList.Count > 0 ? urlList.ToArray() : null;

                } // foreach (XmlNode node in statusNod...
            } // if (dom != null)

            return (tweetsList.Count > 0) ? tweetsList.ToArray() : null;
        }

        /// <summary>
        /// Get timeline data in JSON.
        /// </summary>
        /// <param name="oauthConsumer">Consumer reference.</param>
        /// <param name="paramItems">Parameter items.</param>
        /// <returns>Array of tweets and related items.</returns>
        private Tweets.Library.Tweet[] getTimelineInJson(OAuthConsumer oauthConsumer, List<QueryParameter> paramItems)
        {
            // All tweets
            Tweet[] tweets = null;

            // Make an API Call (call the home_timeline status) and debug the response
            string json = (string)oauthConsumer.Request("http://api.twitter.com/1/statuses/user_timeline.json", "GET", paramItems, ResponseFormat.Json);
            if (string.IsNullOrEmpty(json) == false)
            {
                tweets = JsonConvert.DeserializeObject<Tweet[]>(json);

            } // if (string.IsNullOrEmpty(j...

            return tweets;
        }

        /// <summary>
        /// Get the timeline data of the specified Twitter account.
        /// </summary>
        /// <param name="accountName">Source twitter account.</param>
        /// <param name="settings">Timeline-specified settings.</param>
        /// <returns>Array of Tweets and their related entities.</returns>
        public Tweets.Library.Tweet[] GetTimeline(string accountName, TweetSettings settings)
        {
            // Create an OAuth consumer
            var oauthConsumer = new OAuthConsumer(base.Configuration, "console");

            //include_entities=true&include_rts=true&screen_name=twitterapi&count=2
            var paramItems = new List<QueryParameter>();
            paramItems.Add(new QueryParameter("include_entities", settings.IncludeEntities.ToString().ToLower()));
            paramItems.Add(new QueryParameter("include_rts", settings.IncludeRetweets.ToString().ToLower()));
            paramItems.Add(new QueryParameter("screen_name", accountName));
            paramItems.Add(new QueryParameter("count", settings.TweetCountToFetch.ToString()));

            // Use JSON (and not XML)
            Tweet[] tweets = getTimelineInJson(oauthConsumer, paramItems);

            return tweets;
        }
    }
}
